%{

#include "callother.h"
#include "compiler_unit.h"

#define MAX_STRING_LEN      1024

static int parse_ident();
static int parse_string();
static int badlex();

%}

 /* shortcut for basic regex */
 /* digit */
D			[0-9]
 /* letter */
L			[a-zA-Z_]
 /* whitespace (including newline) */
S			[ \t\v\f\r\n]

 /* exclusive condition for comment */
%x COMMENT

 /* prevent the need for linking with -lfl */
%option noyywrap

%%
<COMMENT>"*/"			{ BEGIN(INITIAL); }
<COMMENT>[^*\n]+		{ /* eat comment in chunks */ }
<COMMENT>"*"			{ /* eat the lone star */ }
<COMMENT>\n			{ /* TODO: lineno */ }
<COMMENT><<EOF>>		{ Compiler_Error("Unterminated comment"); BEGIN(INITIAL); yyterminate(); }
"/*"				{ BEGIN(COMMENT); }
"//"[^\n]*			{ /* ignore comment */ }

{L}({L}|{D})*			{ return parse_ident(); }

\"(\\(.|\n)|[^\\"\n])*\"	{ return parse_string(); }

"+"				{ return '+'; }
"="				{ return '='; }
","				{ return ','; }
";"				{ return ';'; }
"("				{ return '('; }
")"				{ return ')'; }
"{"				{ return '{'; }
"}"				{ return '}'; }


\n				{ /* TODO: lineno */ }
[ \t\v\f\r]			{ /* ignore whitespace */ }
.				{ return badlex(); }

%%

/*
 * *** WARNING *** Keywords should be sorted in alphabet order.
 */
static struct {
    char *word;
    int token;
} g_keywords[] = {
	{"return", L_RETURN},
	{"string", L_STRING_TYPE},
	{"void", L_VOID_TYPE},
};

#define KEYWORD_SIZE (sizeof(g_keywords)/sizeof(g_keywords[0]))

static int lookup_keyword(char *word)
{
    int l = 0;
    int h = KEYWORD_SIZE - 1;
    while (l <= h) {
        int m = (l+h)/2;
        int comp = strcmp(word, g_keywords[m].word);
        if (comp < 0) {
            h = m-1;
        }
        else if (comp == 0) {
            return g_keywords[m].token;
        }
        else {
            l = m+1;
        }
    }
    return 0;
}

static int parse_ident()
{
    int token;

    token = lookup_keyword(yytext);
    if (token != 0) {
        debug(LEX, "keyword (%s)", yytext);
        return token;
    }
    debug(LEX, "L_IDENTIFIER (%s)", yytext);
    yylval.value = CoString_FromString(yytext);
	return L_IDENTIFIER;
}

static int parse_string()
{
    static char buf[MAX_STRING_LEN];
    char *in_start, *out_start, *in_end;

    in_start = yytext + 1;
    if (yyleng - 2 >= MAX_STRING_LEN) {
        /* TODO: string too long */
        in_end = yytext + 1 + MAX_STRING_LEN;
    }
    else {
        in_end = yytext + yyleng - 1;
    }
    out_start = buf;

    while (in_start < in_end) {
        if (*in_start == '\\') {
            in_start++;
            switch(*in_start) {
                case 'n': *out_start++ = '\n'; break;
                case 't': *out_start++ = '\t'; break;
                case 'r': *out_start++ = '\r'; break;
                case 'b': *out_start++ = '\b'; break;
                case '"': *out_start++ = *in_start; break;
                default:
                          *out_start++ = *in_start;
                          /* TODO: Unknown \\x char */
            }
        }
        else {
            *out_start++ = *in_start;
        }
        in_start++;
    }

    yylval.value = CoString_FromStringAndSize(buf, out_start - buf);
    return L_STRING;
}

static int badlex()
{
	char buf[100];

	sprintf(buf, "Illegal character (hex %02x) '%c'", (unsigned)yytext[0], (char)yytext[0]);

	Compiler_Error(buf);

	return ' ';
}

void parse_new_file(int fd)
{
    int yyparse(void); /* grammar.y */
	FILE *file = fdopen(fd, "r");
    YY_BUFFER_STATE yybuf;

    yybuf = yy_create_buffer(file, YY_BUF_SIZE);
	yypush_buffer_state(yybuf);

    BEGIN(INITIAL);
    yyparse();

	yypop_buffer_state();

    fclose(file);
}
